//
// Created by huafv on 10/5/24.
//
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/types.h>
#include <linux/ide.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>

/**********************   defines   **********************/

/**********************   defines   **********************/
#define TIMER_CNT		1		/* device cnt     */
#define TIMER_NAME		"timer"	/* device name 	  */
#define CLOSE_CMD 		(_IO(0XEF, 0x1))	/* off timer */
#define OPEN_CMD		(_IO(0XEF, 0x2))	/* open timer */
#define SET_PERIOD_CMD	(_IO(0XEF, 0x3))	/* set timer per */
#define LED_ON 			1		/* led on */
#define LED_OFF 		0		/* led off */

/**********************   struct defines   **********************/
struct core_timer_dev{
    dev_t dev_id;           /* dev   id */
    int major;              /* major id */
    int minor;              /* minor id */
    struct cdev cdev;       /* cdev     */
    struct class *class;
    struct device *device;
    struct device_node * node;
    spinlock_t lock;
    struct timer_list timer;
    int led_gpio;			/* key所使用的GPIO编号		*/
    int timer_period; 		/* 定时周期,单位为ms */
};

struct core_timer_dev timer_dev;
/**********************   Function prototype   **********************/
static int __init  huafv_timer_init(void);
static void __exit huafv_timer_exit(void);
void timer_callback_func(unsigned long data);
static int timer_open(struct inode *inode, struct file *filp);
static int timer_release(struct inode *inode, struct file *filp);
static long timer_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
static int user_led_init(void);

/**********************   user defines   **********************/
struct file_operations timer_fops = {
    .owner = THIS_MODULE,
    .open = timer_open,
    .unlocked_ioctl = timer_unlocked_ioctl,
    .release = timer_release,
};

/* module init and exit */
module_init(huafv_timer_init);
module_exit(huafv_timer_exit);

/* license and author */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("huafv");

/**********************   Function implementation   **********************/
static int __init  huafv_timer_init(void) {
    printk(KERN_INFO "Huafv timer init\n");

    spin_lock_init(&timer_dev.lock);
    timer_dev.major = 0;

    /* register device */
    if (timer_dev.major) {
        timer_dev.dev_id = MKDEV(timer_dev.major, 0);
        register_chrdev_region(timer_dev.dev_id, TIMER_CNT, TIMER_NAME);
    } else {
        alloc_chrdev_region(&timer_dev.dev_id, 0, TIMER_CNT, TIMER_NAME);
        timer_dev.major = MAJOR(timer_dev.dev_id);	/* 获取分配号的主设备号 */
        timer_dev.minor = MINOR(timer_dev.dev_id);	/* 获取分配号的次设备号 */
    }


    /* init cdev */
    timer_dev.cdev.owner = THIS_MODULE;
    cdev_init(&timer_dev.cdev, &timer_fops);

    /* add cdev */
    cdev_add(&timer_dev.cdev, timer_dev.dev_id, TIMER_CNT);

    /* add class */
    timer_dev.class = class_create(THIS_MODULE, TIMER_NAME);
    if (IS_ERR(timer_dev.class)) {
        return PTR_ERR(timer_dev.class);
    }

    /* add device */
    timer_dev.device = device_create(timer_dev.class, NULL, timer_dev.dev_id, NULL, TIMER_NAME);
    if (IS_ERR(timer_dev.device)) {
        return PTR_ERR(timer_dev.device);
    }

    /* init timer */
    init_timer(&timer_dev.timer);
    timer_dev.timer.function = timer_callback_func;
    timer_dev.timer.data = (unsigned long)&timer_dev;
    return 0;

}

static void __exit huafv_timer_exit(void) {
    printk(KERN_INFO "Huafv timer exit\n");
    printk(KERN_INFO "Huafv timer release\n");
    gpio_set_value(timer_dev.led_gpio, 1);
    gpio_free(timer_dev.led_gpio);
    del_timer_sync(&timer_dev.timer);		/* 删除timer */
    device_destroy(timer_dev.class, timer_dev.dev_id);
    class_destroy(timer_dev.class);
    cdev_del(&timer_dev.cdev);
    unregister_chrdev_region(timer_dev.dev_id, TIMER_CNT);
}

void timer_callback_func(unsigned long data){
    struct core_timer_dev *dev = (struct core_timer_dev *)data;
    static int sta = 1;
    unsigned long flags;
    // printk(KERN_INFO "Huafv timer func call\n");

    sta = !sta;
    gpio_set_value(dev->led_gpio, sta);

    spin_lock_irqsave(&dev->lock, flags);
    spin_unlock_irqrestore(&dev->lock, flags);
    mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->timer_period));
}

static int timer_open(struct inode *inode, struct file *filp)
{
    int ret = 0;
    filp->private_data = &timer_dev;

    timer_dev.timer_period = 1000;
    ret = user_led_init();
    if (ret < 0) {
        return ret;
    }

    return 0;
}

static long timer_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    struct core_timer_dev *dev =  (struct core_timer_dev *)filp->private_data;
    int timer_period;
    unsigned long flags;

    switch (cmd) {
        case CLOSE_CMD:
            del_timer_sync(&dev->timer);
        break;
        case OPEN_CMD:
            spin_lock_irqsave(&dev->lock, flags);
            timer_period = dev->timer_period;
            spin_unlock_irqrestore(&dev->lock, flags);
            mod_timer(&dev->timer, jiffies + msecs_to_jiffies(timer_period));
        break;
        case SET_PERIOD_CMD:
            spin_lock_irqsave(&dev->lock, flags);
            dev->timer_period = (int)arg;
            spin_unlock_irqrestore(&dev->lock, flags);
            mod_timer(&dev->timer, jiffies + msecs_to_jiffies(arg));
        break;
        default:
            break;
    }
    return 0;
}

static int user_led_init(void)
{
    int ret = 0;

    timer_dev.node = of_find_node_by_path("/huafvgpio");
    if (timer_dev.node== NULL) {
        return -EINVAL;
    }

    timer_dev.led_gpio = of_get_named_gpio(timer_dev.node ,"led-gpios", 0);
    if (timer_dev.led_gpio < 0) {
        printk("can't get led\r\n");
        return -EINVAL;
    }

    gpio_request(timer_dev.led_gpio, "led");
    ret = gpio_direction_output(timer_dev.led_gpio, 1);
    if(ret < 0) {
        printk("can't set gpio!\r\n");
    }
    return 0;
}

static int timer_release(struct inode *inode, struct file *filp) {
    printk(KERN_INFO "Huafv timer release\n");
    return 0;
}

// /home/huafv/linux/nfs/rootfs/lib/modules/4.1.15-g3dc0a4b/
